﻿using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq.Expressions;
using TfsUtils.Utils;


namespace TfsUtils.Ui.Mvvm.Command
{
	public static class PropertyChangedObserver
	{
		public static PropertyChangedMonitor<T> Monitor<T>( T source )
			where T : class, INotifyPropertyChanged
		{
			return new PropertyChangedMonitor<T>( source );
		}
	}

	public class PropertyChangedMonitor<T> : AbstractMonitor<T>,
		INotifyPropertyChanged
		where T : class, INotifyPropertyChanged
	{
		public event PropertyChangedEventHandler PropertyChanged;

		protected virtual void OnPropertyChanged( PropertyChangedEventArgs args )
		{
			if( this.PropertyChanged != null )
			{
				this.PropertyChanged( this, args );
			}
		}

		IDictionary<String, Action<T, String>> propertiesToWatch = new Dictionary<String, Action<T, String>>();
		PropertyChangedEventHandler handler = null;

		public PropertyChangedMonitor( T source )
			: base( source )
		{
			handler = ( s, e ) =>
			{
				Action<T, String> callback;
				if( propertiesToWatch.TryGetValue( e.PropertyName, out callback ) )
				{
					if( callback != null )
					{
						callback( source, e.PropertyName );
					}
				}

				if( propertiesToWatch.ContainsKey( e.PropertyName ) )
				{
					this.OnPropertyChanged( e );
					this.OnChanged();
				}
			};

			this.WeakSource.Target.PropertyChanged += handler;
		}

		public PropertyChangedMonitor<T> HandleChangesOf<TProperty>( Expression<Func<T, TProperty>> property )
		{
			this.HandleChangesOf( property, null );

			return this;
		}

		public PropertyChangedMonitor<T> HandleChangesOf<TProperty>( Expression<Func<T, TProperty>> property, Action<T, String> callback )
		{
			var propertyName = property.GetMemberName();
			if( propertiesToWatch.ContainsKey( propertyName ) )
			{
				propertiesToWatch[ propertyName ] = callback;
			}
			else
			{
				propertiesToWatch.Add( propertyName, callback );
			}

			return this;
		}

		public PropertyChangedMonitor<T> DismissHandlingOf<TProperty>( Expression<Func<T, TProperty>> property )
		{
			var propertyName = property.GetMemberName();
			if( propertiesToWatch.ContainsKey( propertyName ) )
			{
				propertiesToWatch.Remove( propertyName );
			}

			return this;
		}

		protected override void OnStopMonitoring()
		{
			if( this.WeakSource != null && this.WeakSource.IsAlive )
			{
				this.WeakSource.Target.PropertyChanged -= handler;
			}
		}
	}
}
